﻿using System;
using System.Text;
using System.Drawing;
using System.Collections.Generic;
using System.Runtime.InteropServices;

using Un4seen.Bass;
using Un4seen.Bass.Misc;

namespace Sirene.Core
{
    public class SireneAction
    {
        public string strFileName = String.Empty;
        public int stream = 0;
        public SireneVisual sv;
        public SireneData sd;

        /// <summary>
        /// Local variables
        /// </summary>        
        private int _tickCounter = 0;
        private int _deviceLatencyMS = 0; // device latency in milliseconds
        private int _deviceLatencyBytes = 0; // device latency in bytes
        private int _updateInterval = 50; // 50ms
        private BASSTimer _updateTimer = null;
        //private SireneData sd;
        
        /* noneunderstandable */
        private int _30mslength = 0;
        private DSPPROC _myDSPAddr = null;
        private SYNCPROC _sync = null;

        /// <summary>
        /// Main load method
        /// </summary>
        /// <param name="ip" see cref="IntPtr ip"/>
        public void Load(IntPtr ip)
        {
            if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT | BASSInit.BASS_DEVICE_LATENCY, ip, Guid.Empty))
            {
                BASS_INFO info = new BASS_INFO();
                Bass.BASS_GetInfo(info);
                _deviceLatencyMS = info.latency;
            }
            else
            {
                Console.WriteLine("Error: Bass Init fail");
            }

            // create a secure timer
            _updateTimer = new BASSTimer(_updateInterval);
            _updateTimer.Tick += new EventHandler(timerUpdate_Tick);

            _sync = new SYNCPROC(EndPosition);
        }

        /// <summary>
        /// Method for play audio stream
        /// </summary>
        public void Play()
        {
            _updateTimer.Stop();
            Bass.BASS_StreamFree(stream);
            if (strFileName != String.Empty)
            {
                // create the stream
                stream = Bass.BASS_StreamCreateFile(strFileName, 0, 0, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_PRESCAN);
                if (stream != 0)
                {
                    _30mslength = (int)Bass.BASS_ChannelSeconds2Bytes(stream, 0.03);
                    _deviceLatencyBytes = (int)Bass.BASS_ChannelSeconds2Bytes(stream, _deviceLatencyMS / 1000.0);
                    // set a DSP user callback method
                    _myDSPAddr = new DSPPROC(MyDSPGain);
                    Bass.BASS_ChannelSetDSP(stream, _myDSPAddr, System.IntPtr.Zero, 2);
                }
            }

            if (stream != 0 && Bass.BASS_ChannelPlay(stream, false))
            {
                sv = new SireneVisual(stream);

                _updateTimer.Start();

                sd = new SireneData(strFileName, stream);
                sd.GetTagsInfo();
            }
            else
            {
                Console.WriteLine("Error={0}", Bass.BASS_ErrorGetCode());
            }
        }

        public void Pause()
        {
            // Stop. Hammertime =)
        }

        public void Next()
        {

        }

        public void Previews()
        {

        }

        public void Exit()
        {
            _updateTimer.Tick -= new EventHandler(timerUpdate_Tick);
            // close bass
            Bass.BASS_Stop();
            Bass.BASS_Free();
        }

        #region MyDSPGain Safe Method
        
        // this will be our local buffer
        // we use it outside of MyDSPGain for better performance and to reduce
        // the need to alloc it everytime MyDSPGain is called!
        private float[] _data;

        // this local member keeps the amplification level as a float value
        private float _gainAmplification = 1;

        // the DSP callback - safe!
        private void MyDSPGain(int handle, int channel, System.IntPtr buffer, int length, System.IntPtr user)
        {
            if (_gainAmplification == 1f || length == 0 || buffer == IntPtr.Zero)
                return;

            // length is in bytes, so the number of floats to process is:
            // length / 4 : byte = 8-bit, float = 32-bit
            int l4 = length / 4;

            // increase the data buffer as needed
            if (_data == null || _data.Length < l4)
                _data = new float[l4];

            // copy from unmanaged to managed memory
            Marshal.Copy(buffer, _data, 0, l4);

            // apply the gain, assumeing using 32-bit floats (no clipping here ;-)
            for (int a = 0; a < l4; a++)
                _data[a] = _data[a] * _gainAmplification;

            // copy back from managed to unmanaged memory
            Marshal.Copy(_data, 0, buffer, l4);
        }
        #endregion
 
        private void timerUpdate_Tick(object sender, System.EventArgs e)
        {
            // here we gather info about the stream, when it is playing...
            if (Bass.BASS_ChannelIsActive(stream) == BASSActive.BASS_ACTIVE_PLAYING)
            {
                // the stream is still playing...
            }
            else
            {
                // the stream is NOT playing anymore...
                _updateTimer.Stop();

                return;
            }

            // from here on, the stream is for sure playing...
            _tickCounter++;
            

            if (_tickCounter == 5)
            {
                // display the position every 250ms (since timer is 50ms)
                _tickCounter = 0;

                sd.GetStreamTiming();
            }


        
            sv.DrawSpectrum();
        }

        private void EndPosition(int handle, int channel, int data, IntPtr user)
        {
            Bass.BASS_ChannelStop(channel);
        }
    }
}
